﻿
#region Credentials

## Use a global variable to store the credential which will be used to access TeamCity by default when no
##	other credential has been specified by the user.

function Get-TCCredential
{
	return $Global:TCCredential;
}

function Set-TCCredential
{
	param (
			[Parameter(Position=1,Mandatory=$false)]
			[System.Management.Automation.PSCredential] $Credential );
	if( -not $Credential )
	{
		$Credential = Get-Credential;
	}

	$Global:TCCredential = $Credential;
}

function Test-IsTCCredential
{
	param (
			[Parameter(Position=1,Mandatory=$true)]
			[Object] $Credential,
			[Switch] $throw);
			
			if( $Credential -eq $null)
			{
				Invoke-Ternary { $throw } { Write-Error "Invalid TeamCity Credential" } { return $false }
			}
			
			if( $Credential.PSObject.TypeNames.Contains("System.Management.Automation.PSCredential") )
			{
				Invoke-Ternary { $throw } { } { return $true }
			}
			else
			{
				Invoke-Ternary { $throw } { Write-Error "Invalid TeamCity Credential" } { return $false }
			}
			
}
#endregion

#region Server Management

## Use a global variable to store the build server which will be used to access TeamCity

function Get-TCServer
{
	return $Global:TCServer;
}

function Set-TCServer
{
	param (
			[Parameter(Position=1,Mandatory=$true)]
			[String] $Server );

	$Global:TCServer = $Server;
}

function Test-IsTCServerSet
{
	param ( [Switch] $throw );
			
			if( [System.String]::IsNullOrEmpty($Global:TCServer) )
			{
				Invoke-Ternary { $throw } { Write-Error "Invalid TeamCity Credential" } { return $false }
			}
			else
			{
				Invoke-Ternary { $throw } { } { return $true }
			}
			
}
#endregion

function Get-TCResponse
{
	param(	[Parameter(Position=1,Mandatory=$true)]
			[String] $URL,
			[Parameter(Position=1,Mandatory=$false)]
			[System.Management.Automation.PSCredential] $Credential = $TCCredential )
	
	Test-IsTCCredential -throw -Credential $Credential | ;
	Test-IsTCServerSet -throw;
	
	# Setup the Web Request
	$WebRequest = [System.Net.WebRequest]::Create($URL);
	$WebRequest.Credentials = $Credential;
	$WebRequest.AuthenticationLevel = [System.Net.Security.AuthenticationLevel]::MutualAuthRequested;
	## There has to be a better way to intercept a 404 error
	try {
		$WebResponse = $WebRequest.GetResponse();
	} 
	catch [Exception]
	{
		if($ERROR[0].Exception.InnerException.Response.StatusCode -eq 'NotFound')
		{
			throw New-Object System.Exception("Object not found on server");
		}
		else
		{
			Write-Error $ERROR[0].Exception.InnerException;
		}
	}
	
	# Read the Web-Request into a XML Document
	$ResponseDoc = new-object System.Xml.XmlDocument;
	
	# Deserialize XML response and load into ResponseDoc object
	$ResponseDoc.Load($WebResponse.GetResponseStream());
	
	# Pass to the pipeline
	 $ResponseDoc | Write-Output;
	
}

filter Get-TCBuildType
{
	param(	[Parameter(Position=1,Mandatory=$false)]
			[String] $id )
	
	$RequestUrl = [System.String]::Format("http://{0}/httpAuth/app/rest/buildTypes/", $Global:TCServer )
	
	if( -not [System.String]::IsNullOrEmpty($id) )
	{
		$RequestUrl += "id:$id";
	}
	
	try {
		$Response = Get-TCResponse -URL $RequestURL;
	}
	catch [Exception]
	{
		Write-Error "Build Type not found on the build server";
	}
	
	$BuildTypes = $Response.SelectNodes("//buildType")

	if( ($BuildTypes | Measure-Object).Count -gt 0 )
	{
		$BuildTypes | % {
				$BuildObject = New-Object PSObject;
				$BuildObject | Add-Member -Name id -MemberType NoteProperty -Value $_.id;
				$BuildObject | Add-Member -Name Name -MemberType NoteProperty -Value $_.name;
				$BuildObject | Add-Member -Name ProjectName -MemberType NoteProperty -Value $_.projectName;
				$BuildObject | Add-Member -Name href -MemberType NoteProperty -Value $_.href;
				$BuildObject.PSObject.TypeNames.Insert(0, ("TC.BuildType"));
				$BuildObject.PSObject.TypeNames.Insert(0, ("TC.BuildType." + $_.id));
				$BuildObject | Write-Output;
			} | Write-Output
	}
}

filter Get-Build
{
	param(	[Parameter(Position=1,ParameterSetName="BuildTypeObject",ValueFromPipeline=$true)]
			[System.Management.Automation.PSObject] $BuildType,
			[Parameter(Position=1,ParameterSetName="BuildTypeId",ValueFromPipeline=$true)]
			[String] $BuildTypeId,
			[Parameter(ParameterSetName="NoBuildType")]
			[Parameter(ParameterSetName="BuildTypeId")]
			[Parameter(ParameterSetName="BuildTypeObject")]
			[string] $Number,
			[Parameter(ParameterSetName="NoBuildType")]
			[Parameter(ParameterSetName="BuildTypeId")]
			[Parameter(ParameterSetName="BuildTypeObject")]
			[Switch] $Success,
			[Parameter(ParameterSetName="NoBuildType")]
			[Parameter(ParameterSetName="BuildTypeId")]
			[Parameter(ParameterSetName="BuildTypeObject")]
			[Switch] $Latest,
			[Parameter(ParameterSetName="NoBuildType")]
			[Parameter(ParameterSetName="BuildTypeId")]
			[Parameter(ParameterSetName="BuildTypeObject")]
			[Switch] $Pinned,
			[Parameter(ParameterSetName="NoBuildType")]
			[Parameter(ParameterSetName="BuildTypeId")]
			[Parameter(ParameterSetName="BuildTypeObject")]
			[int] $Count = 25,
			[Parameter(ParameterSetName="NoBuildType")]
			[switch] $All)
	
	if($Latest)
	{
		$Count = 1;
	}
	
	# Create the appropriate URL path to TeamCity's REST API based on parameter supplied
	switch ($PsCmdlet.ParameterSetName)
	{
		"BuildTypeObject" { $BuildTypeId = $BuildType.id ; break }
	}
	
	$BuildInfoUrl = [System.String]::Format('http://{0}/httpAuth/app/rest/builds/?locator=count:{2}',$Global:TCServer,$BuildTypeId,$Count )
	
	if(-not ([System.String]::IsNullOrEmpty($BuildTypeId)) )
	{
		$BuildInfoUrl = $BuildInfoUrl + ",buildType:$BuildTypeId";
	}
	
	# If ShowAll switch WASN'T thrown, tack the status onto the URL to only return Successful builds
	if($Success)
	{
		$BuildInfoUrl = $BuildInfoUrl + ",status:SUCCESS";
	}
	
	if($Pinned)
	{
		$BuildInfoUrl = $BuildInfoUrl + ",pinned:true";
	}
	
	$ErrorActionPreference = 'stop';
	

	# Call Get-BuildServerResponse passing the URL which will return an XML Document
	$ResponseDoc = Get-BuildServerResponse -URL $BuildInfoUrl
	
	# Select only /builds/build nodes of that XML document and store in $Builds variable
	$Builds = $ResponseDoc.builds.SelectNodes("/builds/build")

	# If the count of build nodes returned are more than Zero...
	if( ($Builds | Measure-Object).Count -gt 0 )
	{
		# For each returned item in $Builds...
		$Builds | % {
				$BuildInfo = Get-BuildServerResponse -URL ([System.String]::Format('http://{0}{1}',$Global:TCServer,$_.href));
				
				$BuildTypeID = $_.buildTypeId;
				$BuildObject = New-Object PSObject;
				$BuildObject | Add-Member -Name id -MemberType NoteProperty -Value $_.id;
				$BuildObject | Add-Member -Name Number -MemberType NoteProperty -Value $_.number;
				$BuildObject | Add-Member -Name Status -MemberType NoteProperty -Value $_.status;
				$BuildObject | Add-Member -Name BuildTypeId -MemberType NoteProperty -Value $_.buildTypeId;
				$BuildObject | Add-Member -Name Pinned -MemberType NoteProperty -Value ([System.Boolean]::Parse($BuildInfo.build.pinned));
				$BuildObject | Add-Member -Name Tags -MemberType NoteProperty -Value ( $BuildInfo.build.tags.tag | % { $_ } );
				$BuildObject.PSObject.TypeNames.Insert(0, ("TC.Build"));
				$BuildObject.PSObject.TypeNames.Insert(0, ("TC.Build." + $_.buildTypeId));
				$BuildObject | Write-Output;
		}
	}
}

